<template>
  <Modal v-bind="getBindValue" @cancel="handleCancel">
    <template #closeIcon v-if="!$slots.closeIcon">
      <ModalClose
        :canFullscreen="getProps.canFullscreen"
        :fullScreen="fullScreenRef"
        @cancel="handleCancel"
        @fullscreen="handleFullScreen"
      />
    </template>

    <template #title v-if="!$slots.title">
      <ModalHeader :helpMessage="getProps.helpMessage" :title="getMergeProps.title" @dblclick="handleTitleDbClick" />
    </template>

    <template #footer v-if="!$slots.footer">
      <ModalFooter v-bind="getBindValue" @ok="handleOk" @cancel="handleCancel">
        <template #[item]="data" v-for="item in Object.keys($slots)">
          <slot :name="item" v-bind="data || {}"></slot>
        </template>
      </ModalFooter>
    </template>

    <ModalWrapper
      :useWrapper="getProps.useWrapper"
      :footerOffset="wrapperFooterOffset"
      :fullScreen="fullScreenRef"
      ref="modalWrapperRef"
      :loading="getProps.loading"
      :loading-tip="getProps.loadingTip"
      :minHeight="getProps.minHeight"
      :height="getWrapperHeight"
      :visible="visibleRef"
      :modalFooterHeight="footer !== undefined && !footer ? 0 : undefined"
      v-bind="omit(getProps.wrapperProps, 'visible', 'height', 'modalFooterHeight')"
      @ext-height="handleExtHeight"
      @height-change="handleHeightChange"
    >
      <slot></slot>
    </ModalWrapper>

    <template #[item]="data" v-for="item in Object.keys(omit($slots, 'default'))">
      <slot :name="item" v-bind="data || {}"></slot>
    </template>
  </Modal>
</template>
<script lang="ts" setup name="BasicModal" inheritAttrs="false">
import type { ModalProps, ModalMethods } from './typing'

import { computed, ref, watch, unref, watchEffect, toRef, getCurrentInstance, nextTick, useAttrs } from 'vue'
import Modal from './components/Modal'
import ModalWrapper from './components/ModalWrapper.vue'
import ModalClose from './components/ModalClose.vue'
import ModalFooter from './components/ModalFooter.vue'
import ModalHeader from './components/ModalHeader.vue'
import { isFunction } from '@/utils/is'
import { deepMerge } from '@/utils'
import { basicProps } from './props'
import { useFullScreen } from './hooks/useModalFullScreen'
import { omit } from 'lodash-es'
import { useDesign } from '@/hooks/web/useDesign'

const props = defineProps(basicProps)
const attrs = useAttrs()
const emit = defineEmits(['visible-change', 'height-change', 'cancel', 'ok', 'register', 'update:visible'])
const visibleRef = ref(false)
const propsRef = ref<Partial<ModalProps> | null>(null)
const modalWrapperRef = ref<any>(null)
const { prefixCls } = useDesign('basic-modal')

// modal   Bottom and top height
const extHeightRef = ref(0)
const modalMethods: ModalMethods = {
  setModalProps,
  emitVisible: undefined,
  redoModalHeight: () => {
    nextTick(() => {
      if (unref(modalWrapperRef)) {
        ;(unref(modalWrapperRef) as any).setModalHeight()
      }
    })
  }
}

const instance = getCurrentInstance()
if (instance) {
  emit('register', modalMethods, instance.uid)
}

// Custom title component: get title
const getMergeProps = computed((): Recordable => {
  return {
    ...props,
    ...(unref(propsRef) as any)
  }
})

const { handleFullScreen, getWrapClassName, fullScreenRef } = useFullScreen({
  modalWrapperRef,
  extHeightRef,
  wrapClassName: toRef(getMergeProps.value, 'wrapClassName')
})

// modal component does not need title and origin buttons
const getProps = computed((): Recordable => {
  const opt = {
    ...unref(getMergeProps),
    visible: unref(visibleRef),
    okButtonProps: undefined,
    cancelButtonProps: undefined,
    title: undefined
  }
  return {
    ...opt,
    wrapClassName: unref(getWrapClassName)
  }
})

const getBindValue = computed((): Recordable => {
  const attr = {
    ...attrs,
    ...unref(getMergeProps),
    visible: unref(visibleRef)
  }
  attr['wrapClassName'] = `${attr?.['wrapClassName'] || ''} ${unref(getWrapClassName)}`
  if (unref(fullScreenRef)) {
    return omit(attr, ['height', 'title'])
  }
  return omit(attr, 'title')
})

const getWrapperHeight = computed(() => {
  if (unref(fullScreenRef)) return undefined
  return unref(getProps).height
})

watchEffect(() => {
  visibleRef.value = !!props.visible
  fullScreenRef.value = !!props.defaultFullscreen
})

watch(
  () => unref(visibleRef),
  (v) => {
    emit('visible-change', v)
    emit('update:visible', v)
    instance && modalMethods.emitVisible?.(v, instance.uid)
    nextTick(() => {
      if (props.scrollTop && v && unref(modalWrapperRef)) {
        ;(unref(modalWrapperRef) as any).scrollTop()
      }
    })
  },
  {
    immediate: false
  }
)

// 取消事件
async function handleCancel(e: Event) {
  e?.stopPropagation()
  // 过滤自定义关闭按钮的空白区域
  if ((e.target as HTMLElement)?.classList?.contains(prefixCls + '-close--custom')) return
  if (props.closeFunc && isFunction(props.closeFunc)) {
    const isClose: boolean = await props.closeFunc()
    visibleRef.value = !isClose
    return
  }

  visibleRef.value = false
  emit('cancel', e)
}

/**
 * @description: 设置modal参数
 */
function setModalProps(props: Partial<ModalProps>): void {
  // Keep the last setModalProps
  propsRef.value = deepMerge(unref(propsRef) || ({} as any), props)
  if (Reflect.has(props, 'visible')) {
    visibleRef.value = !!props.visible
  }
  if (Reflect.has(props, 'defaultFullscreen')) {
    fullScreenRef.value = !!props.defaultFullscreen
  }
}

function handleOk(e: Event) {
  emit('ok', e)
}

function handleHeightChange(height: string) {
  emit('height-change', height)
}

function handleExtHeight(height: number) {
  extHeightRef.value = height
}

function handleTitleDbClick(e) {
  if (!props.canFullscreen) return
  e.stopPropagation()
  handleFullScreen(e)
}
</script>
